/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#include "Tpm.h"

#if CC_PolicyAuthorizeNV  // Conditional expansion of this file

#include "PolicyAuthorizeNV_fp.h"
#include "Policy_spt_fp.h"
#include "Marshal.h"

/*(See part 3 specification)
// Change policy by a signature from authority
*/
//  Return Type: TPM_RC
//      TPM_RC_HASH         hash algorithm in 'keyName' is not supported or is not
//                          the same as the hash algorithm of the policy session
//      TPM_RC_SIZE         'keyName' is not the correct size for its hash algorithm
//      TPM_RC_VALUE        the current policyDigest of 'policySession' does not
//                          match 'approvedPolicy'; or 'checkTicket' doesn't match
//                          the provided values
/*
将某个授权值保存在 NV 中。
使用时需要读取 NV 值与当前策略计算得到的 digest 比较，如果匹配则验证通过，计算并更新 digest。
当要撤销这个策略时，只需要修改 NV 值。

tpm2_policyauthorizenv(1) - This command allows for policies to change by referencing the authorization policy written to an NV index. 
The NV index containing the authorization policy should remain readable even for trial session. The index can be specified as raw 
handle or an offset value to the nv handle range "TPM2_HR_NV_INDEX".
tpm2_policyauthorizenv(1)-这个命令允许通过引用写入 NV 索引的授权策略来更改策略。包含授权策略的 NV 索引即使在试用期也应保持可读性。
索引可以指定为原始句柄或 nv 句柄范围“TPM2_HR_NV_INDEX”的偏移量值。

This command provides a capability that is the equivalent of a revocable policy. With TPM2_PolicyAuthorize(), 
the authorization ticket never expires, so the authorization may not be withdrawn. With this command, the approved 
policy is kept in an NV Index location so that the policy may be changed as needed to render the old policy unusable.
此命令提供相当于可撤销策略的功能。 
使用 TPM2_PolicyAuthorize()，授权票证永不过期，因此授权可能不会被撤销。 
使用此命令，批准的策略将保存在 NV 索引位置，以便可以根据需要更改策略以使旧策略不可用.(修改 NV 值)

NOTE 1 This command is useful for Objects but of limited value for other policies that are persistently stored in TPM NV, such as the OwnerPolicy.
An authorization session providing authorization to read the NV Index shall be provided.
The authorization to read the NV Index must succeed even if policySession is a trial policy session.
If policySession is a trial policy session, the TPM will update policySession→policyDigest as shown in equation (40) below and return TPM_RC_SUCCESS. 
It will not perform any further validation. The remainder of this general description would apply only if policySession is not a trial policy session.
NOTE 2 If read access is controlled by policy, the policy should include a branch that authorizes a TPM2_PolicyAuthorizeNV().

注 1 此命令对对象很有用，但对持久存储在 TPM NV 中的其他策略（例如 OwnerPolicy）的价值有限。
应提供提供读取 NV 索引授权的授权会话。
即使 policySession 是试用策略会话，读取 NV 索引的授权也必须成功。
如果 policySession 是一个试用策略会话，TPM 将更新 policySession→policyDigest，如下面的等式 (40) 所示，并返回 TPM_RC_SUCCESS。 
它不会执行任何进一步的验证。 仅当 policySession 不是试用策略会话时，此一般描述的其余部分才适用。
注 2 如果读访问由策略控制，该策略应包括授权 TPM2_PolicyAuthorizeNV() 的分支。

*/
TPM_RC
TPM2_PolicyAuthorizeNV(
    PolicyAuthorizeNV_In    *in
    )
{
    SESSION                 *session;
    TPM_RC                   result;
    NV_REF                   locator;
    NV_INDEX                *nvIndex = NvGetIndexInfo(in->nvIndex, &locator);
    TPM2B_NAME               name;
    TPMT_HA                  policyInNv;
    BYTE                     nvTemp[sizeof(TPMT_HA)];
    BYTE                    *buffer = nvTemp;
    INT32                    size;

// Input Validation
    // Get pointer to the session structure
    session = SessionGet(in->policySession);

    // Skip checks if this is a trial policy
    if(!session->attributes.isTrialPolicy)
    {
        // Check the authorizations for reading
        // Common read access checks. NvReadAccessChecks() returns
        // TPM_RC_NV_AUTHORIZATION, TPM_RC_NV_LOCKED, or TPM_RC_NV_UNINITIALIZED
        // error may be returned at this point
        result = NvReadAccessChecks(in->authHandle, in->nvIndex,
                                    nvIndex->publicArea.attributes);
        if(result != TPM_RC_SUCCESS)
            return result;

        // Read the contents of the index into a temp buffer
        size = MIN(nvIndex->publicArea.dataSize, sizeof(TPMT_HA));
        NvGetIndexData(nvIndex, locator, 0, (UINT16)size, nvTemp);

        // Unmarshal the contents of the buffer into the internal format of a
        // TPMT_HA so that the hash and digest elements can be accessed from the
        // structure rather than the byte array that is in the Index (written by
        // user of the Index).
        result = TPMT_HA_Unmarshal(&policyInNv, &buffer, &size, FALSE);
        if(result != TPM_RC_SUCCESS)
            return result;

        // Verify that the hash is the same
        if(policyInNv.hashAlg != session->authHashAlg)
            return TPM_RC_HASH;

        // See if the contents of the digest in the Index matches the value
        // in the policy
        if(!MemoryEqual(&policyInNv.digest, &session->u2.policyDigest.t.buffer,
                        session->u2.policyDigest.t.size))
            return TPM_RC_VALUE;
    }

// Internal Data Update

    // Set policyDigest to zero digest
    PolicyDigestClear(session);

    // Update policyDigest
    PolicyContextUpdate(TPM_CC_PolicyAuthorizeNV, EntityGetName(in->nvIndex, &name),
                        NULL, NULL, 0, session);

    return TPM_RC_SUCCESS;
}

#endif // CC_PolicyAuthorize